package nmea2000

import "github.com/project-flogo/core/data/coerce"

import "fmt"

type Settings struct {
}

type Input struct {
	Frame []byte `md:"frame"` // The message to log
}

func (i *Input) ToMap() map[string]interface{} {
	return map[string]interface{}{
		"frame": i.Frame,
	}
}

func (i *Input) FromMap(values map[string]interface{}) error {

	var err error
	i.Frame, err = ToBytes(values["frame"])
	if err != nil {
		return err
	}
	return nil
}

func ToBytes(val interface{}) ([]byte, error) {

	switch t := val.(type) {
	case []byte:
		return t, nil
	case []interface{}:
		bs := make([]byte, len(t))
		for idx, elem := range t {
			b, err := ToInt(elem)
			if err != nil {
				return []byte{}, err
			}
			bs[idx] = byte(b)
		}
		return bs, nil
	case string:
		return []byte(t), nil
	case nil:
		return nil, nil
	default:
		//for now just convert everything to string then bytes
		s, err := coerce.ToString(val)
		if err != nil {
			return nil, fmt.Errorf("unable to coerce %#v to bytes", t)
		}
		return []byte(s), nil
	}
}

func ToInt(val interface{}) (int, error) {
	switch t := val.(type) {
	case int:
		return t, nil
	case int8:
		return int(t), nil
	case int16:
		return int(t), nil
	case int32:
		return int(t), nil
	case int64:
		return int(t), nil
	case uint:
		return int(t), nil
	case uint8:
		return int(t), nil
	case uint16:
		return int(t), nil
	case uint32:
		return int(t), nil
	case uint64:
		return int(t), nil
	case float64:
		return int(t), nil
	case bool:
		if t {
			return 1, nil
		}
		return 0, nil
	case nil:
		return 0, nil
	default:
		return 0, fmt.Errorf("unable to coerce %#v to int", val)
	}
}

type Output struct {
	RawFrame []byte `md:"rawFrame,omitempty"`
	CANID    uint32 `md:"canid,omitempty"`
	PGN      uint32 `md:"pgn,omitempty"`
	Prio     uint32 `md:"prio,omitempty"`
	SRC      uint32 `md:"src,omitempty"`
	DST      uint32 `md:"dst,omitempty"`
}

func (r *Output) ToMap() map[string]interface{} {
	return map[string]interface{}{
		"rawFrame": r.RawFrame,
		"canId":    r.CANID,
		"pgn":      r.PGN,
		"prio":     r.Prio,
		"src":      r.SRC,
		"dst":      r.DST,
	}
}

func (r *Output) FromMap(values map[string]interface{}) error {
	var err error
	r.RawFrame, err = ToBytes(values["rawFrame"])
	if err != nil {
		return err
	}

	canId, err := coerce.ToInt32(values["canId"])
	if err != nil {
		return err
	}
	r.CANID = uint32(canId)

	pgn, err := coerce.ToInt32(values["pgn"])
	if err != nil {
		return err
	}
	r.PGN = uint32(pgn)

	prio, err := coerce.ToInt32(values["prio"])
	if err != nil {
		return err
	}
	r.Prio = uint32(prio)

	src, err := coerce.ToInt32(values["src"])
	if err != nil {
		return err
	}
	r.SRC = uint32(src)

	dst, err := coerce.ToInt32(values["dst"])
	if err != nil {
		return err
	}
	r.DST = uint32(dst)

	return nil
}
